using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

// Grid 结构体表示扫雷游戏中的“网格”数据模型
// 它以高效、并行的方式管理每个格子的状态（如是否翻开、是否为地雷、是否被标记等）
// 使用 Unity 的 Jobs 系统和 NativeArray 来实现高性能、多线程的网格操作，特别适合大规模格子计算
public struct Grid
{
    // 索引器，允许通过整数索引 i 直接访问或修改某个格子的状态（CellState）
    // 例如：grid[i] = CellState.Revealed;
    public CellState this[int i]
    {
        get => states[i];         // 获取第 i 个格子的状态
        set => states[i] = value; // 设置第 i 个格子的状态
    }

    public int Rows { get; private set; }       // 网格的行数
    public int Columns { get; private set; }    // 网格的列数

    // CellCount 是网格中总的格子数量，即行数 × 列数
    public int CellCount => states.Length;

    // HiddenCellCount 是当前还未被翻开的格子数量，即总格子数 - 已翻开格子数
    public int HiddenCellCount => CellCount - RevealedCellCount;

    // RevealedCellCount 是当前已经被翻开的格子数量，使用 NativeArray<int>[0] 存储，保证线程安全
    public int RevealedCellCount
    {
        get => revealedCellCount[0];     // 获取已翻开格子数量
        set => revealedCellCount[0] = value; // 设置已翻开格子数量
    }

    NativeArray<int> revealedCellCount;     // 用于存储已翻开格子数量的 NativeArray，线程安全且高效
    NativeArray<CellState> states;          // 存储每个格子状态的 NativeArray，每个元素是一个 CellState 枚举值

    /// <summary>
    /// 初始化网格，设置行数、列数，并分配 NativeArray 内存
    /// </summary>
    public void Initialize(int rows, int columns)
    {
        Rows = rows;
        Columns = columns;

        // 创建一个长度为 1 的 NativeArray，用于存储已翻开格子的计数
        revealedCellCount = new NativeArray<int>(1, Allocator.Persistent);

        // 创建一个长度为 Rows * Columns 的 NativeArray，用于存储每个格子的状态
        states = new NativeArray<CellState>(Rows * Columns, Allocator.Persistent);
    }

    /// <summary>
    /// 释放 NativeArray 占用的非托管内存，防止内存泄漏
    /// </summary>
    public void Dispose()
    {
        revealedCellCount.Dispose();
        states.Dispose();
    }

    // 根据行号 row 和列号 column，计算该格子在一维数组 states 中的索引
    public int GetCellIndex(int row, int column) => row * Columns + column;

    
    // 判断给定的行号和列号是否在有效范围内，如果有效则返回对应的索引，否则返回 -1
    public bool TryGetCellIndex(int row, int column, out int index)
    {
        bool valid = 0 <= row && row < Rows && 0 <= column && column < Columns;
        index = valid ? GetCellIndex(row, column) : -1; // 若坐标合法，计算索引；否则索引为 -1
        return valid;
    }

    /// <summary>
    /// 根据一维索引 index，计算出对应的行号 row 和列号 column
    /// </summary>
    public void GetRowColumn(int index, out int row, out int column)
    {
        row = index / Columns;          // 行号 = 索引 / 列数
        column = index - row * Columns; // 列号 = 索引 - (行号 * 列数)
    }

    // 在网格中随机布置指定数量的地雷
    // 使用 Jobs 系统并行化地雷布置过程，提升性能，尤其适用于大地图
    public void PlaceMines(int mines)
    {
        // 创建一个 PlaceMinesJob，设置当前网格、地雷数量以及一个随机种子
        new PlaceMinesJob
        {
            grid = this,
            mines = mines,
            seed = Random.Range(1, int.MaxValue) // 随机种子，确保每次布置的地雷位置不同
        }
        .Schedule().Complete(); // 调度该 Job，并等待其完成（阻塞当前线程，直到任务完成）
    }

    // 翻开指定索引的格子，并触发周围区域的连锁展开（如果该格子周围没有地雷）
    // 使用 Job 系统异步执行展开逻辑，提高效率
    public void Reveal(int index)
    {
        var job = new RevealRegionJob
        {
            grid = this
        };

        // 计算该索引对应的行号和列号，作为展开的起始位置
        GetRowColumn(index, out job.startRowColumn.x, out job.startRowColumn.y);

        // 调度该 Job（异步执行），并等待其完成
        job.Schedule().Complete();
    }

    // 将所有地雷格子以及误标记（点击后爆炸）的格子进行显示（通常在游戏结束时调用）
    // 使用 ParallelFor 批量处理所有格子，大幅提升性能
    public void RevealMinesAndMistakes()
    {
        // 创建一个 RevealMinesAndMistakesJob，设置当前网格
        new RevealMinesAndMistakesJob
        {
            grid = this
        }
        // 使用 ScheduleParallel 并行调度该 Job，以列数为批次大小，并行处理所有格子
        .ScheduleParallel(CellCount, Columns, default).Complete();
    }
}